home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / ARGONET / PD / SOUND / REPLAYER.SPK / c / playraw < prev    next >
Text File  |  1998-08-20  |  12KB  |  409 lines

  1.  
  2. /* playraw.c
  3.  
  4.    Replayer -- audio player
  5.    Copyright (c) 1997/8 Mark Seaborn <mseaborn@argonet.co.uk>
  6.  
  7.    This provides a command line front end to the Replay sound drivers.  It
  8.    can pipe a given file through a specified driver.  While potentially
  9.    useful in its own right, it also acts as an example of how to use the
  10.    Replay sound drivers.  It doesn't rely on RISC OS directly, since it uses
  11.    the replaydriver interface; however, defining `RISCOS' will enable
  12.    TaskWindow idling.
  13. */
  14.  
  15. #include <stdlib.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <assert.h>
  19.  
  20. #ifdef RISCOS
  21. #include "OS:taskwindow.h"
  22. #endif
  23.  
  24. #ifdef MemCheck_MEMCHECK
  25. #include "MemCheck:MemCheck.h"
  26. #endif
  27.  
  28. #include "Replayer:replaydriver.h"
  29.  
  30.  
  31. /* Declarations */
  32.  
  33. #define tool    "playraw"
  34.  
  35. #define DEFAULT_FREQUENCY    11025
  36. #define DEFAULT_BLOCK_TIME    200
  37.  
  38. static void usage(void);
  39. static void finalise(void);
  40.  
  41. typedef struct sound_type sound_type;
  42. struct sound_type {
  43.   int precision, channels;
  44.   unsigned linear_sound: 1;
  45.   unsigned unsigned_sound: 1;
  46. };
  47.  
  48. sound_type *soundtype_create(void);
  49. void soundtype_getopt(sound_type *obj, const char *opt);
  50.  
  51.  
  52. /* Variables for playback */
  53.  
  54. /* These are static because 1) they need to be freed on exit (when escape is
  55.    pressed), and 2) I'm can't be bothered putting them into a structure
  56.    (this is only a command-line tool, it doesn't have to be reusable). */
  57.  
  58. static bool is_playing = 0;
  59. static replaydriver_driver *driver = 0;
  60. static replaydriver_control *control = 0;
  61. static replaydriver_buffer *buffer = 0;
  62. static replaydriver_timecheck_state time_check = 0;
  63. static FILE *file = 0;
  64.  
  65.  
  66. /* Functions */
  67.  
  68. /* Print help message. */
  69. static void usage(void)
  70. {
  71.   fprintf(stderr, "Usage:  %s [options] [--] filename\n\n"
  72.  
  73.     "Where available options are:\n"
  74.     "  -v, --verbose   Print info just before playing\n"
  75.     "  -f, --freq x    Frequency of sound in Hz (default %i Hz)\n"
  76.     "  -h, --help      Show this message\n"
  77.     "  filename        File to read from (`-' for stdin)\n"
  78.  
  79.     "\nChoosing a driver directly:\n"
  80.     "  -d, --driver x  Pathname/leafname of driver to use\n"
  81.     "  -b, --block x   Size of blocks to read each time (bytes)\n"
  82.     "  -u, --buffer x  Size of each buffer\n"
  83.     "  Getting the buffer size wrong can have serious consequences!\n"
  84.  
  85.     "\nChoosing a driver by sound type:\n"
  86.     "  -O  Followed immediately by character options:\n"
  87.     "    8  8-bit sample width\n"
  88.     "    1  16-bit sample width\n"
  89.     "    l  Linear encoding\n"
  90.     "    U  uLaw encoding\n"
  91.     "    s  Signed data\n"
  92.     "    u  Unsigned data\n"
  93.     "    M  Monophonic\n"
  94.     "    S  Stereo\n"
  95.     "  -b, --block x  Size of blocks to read each time (bytes); or\n"
  96.     "  -t, --time x   Time size of blocks to read (centiseconds)\n"
  97.     "Default is -O8lsM --time %i\n"
  98.  
  99.     "\n", tool, DEFAULT_FREQUENCY, DEFAULT_BLOCK_TIME);
  100. }
  101.  
  102. int main(int argc, char *argv[])
  103. {
  104.   /* Misc variables. */
  105.   int i, next_arg = argc, code = EXIT_SUCCESS;
  106.   bool verbose = 0;
  107.   double frequency = DEFAULT_FREQUENCY;
  108.   size_t block_size = 0;
  109.   /* Driver specified directly: */
  110.   const char *driver_name = 0;
  111.   size_t buffer_size = 0;
  112.   /* Driver specified by sound type: */
  113.   int block_time = 0;
  114.   sound_type *stype;
  115.   /* Local variables for playback. */
  116.   bool feed_error = 0;
  117.  
  118. #ifdef MemCheck_MEMCHECK
  119.   MemCheck_Init();
  120.   MemCheck_RegisterArgs(argc, argv);
  121.   MemCheck_InterceptSCLStringFunctions();
  122.   MemCheck_SetStoreMallocFunctions(1);
  123. #endif
  124.  
  125.   stype = soundtype_create();
  126.   if(!stype) {
  127.     fprintf(stderr, "%s: malloc failed before start\n", tool);
  128.     return EXIT_FAILURE;
  129.   }
  130.  
  131.   /* Parse the arguments we're given. */
  132.   for(i=1; i<argc; ++i) {
  133.     if(argv[i][0] == '-' && argv[i][1]) {
  134.       const char *c;
  135.  
  136.       /* Long options, ie. `--foo'. */
  137.       if(argv[i][1] == '-' && argv[i][2]) {
  138.         const char *arg = argv[i] + 2;
  139.         if(!strcmp(arg, "verbose")) { verbose = 1; continue; }
  140.         if(!strcmp(arg, "freq")) { frequency = atof(argv[++i]); continue; }
  141.         if(!strcmp(arg, "help")) { usage(); return EXIT_SUCCESS; }
  142.         if(!strcmp(arg, "block")) { block_size = atoi(argv[++i]); continue; }
  143.         if(!strcmp(arg, "driver")) { driver_name = argv[++i]; continue; }
  144.         if(!strcmp(arg, "buffer"))
  145.             { buffer_size = atoi(argv[++i]); continue; }
  146.         if(!strcmp(arg, "time")) { block_time = atoi(argv[++i]); continue; }
  147.         fprintf(stderr, "%s: Unknown argument, `%s'\n", tool, argv[i]);
  148.         return EXIT_FAILURE;
  149.       }
  150.       /* Short options, ie. `-f -v' or combined, `-fv'. */
  151.       if(argv[i][1] != '-') {
  152.         for(c=argv[i]+1; *c; ++c) switch(*c) {
  153.           case 'v':  verbose = 1; continue;
  154.           case 'f':  frequency = atoi(argv[++i]); continue;
  155.           case 'h':  usage(); return EXIT_SUCCESS;
  156.           case 'b':  block_size = atoi(argv[++i]); continue;
  157.           case 'd':  driver_name = argv[++i]; continue;
  158.           case 'u':  buffer_size = atoi(argv[++i]); continue;
  159.           case 't':  block_time = atoi(argv[++i]); continue;
  160.           case 'O':  soundtype_getopt(stype, c + 1); goto finish_shorts;
  161.           default:
  162.             fprintf(stderr, "%s: Unknown argument, `-%c'\n", tool, *c);
  163.             return EXIT_FAILURE;
  164.         }
  165.       finish_shorts:
  166.         continue;
  167.       }
  168.     }
  169.     /* The rest are non-switch arguments. */
  170.     next_arg = i;
  171.     /* If this is the switch terminator `--', move on one. */
  172.     if(argv[i][0] == '-' && argv[i][1] == '-' && !argv[i][2]) ++next_arg;
  173.     break;
  174.   }
  175.   /* Complain if no filename was given. */
  176.   if(next_arg >= argc) {
  177.     fprintf(stderr, "%s: No filename given\n\n", tool);
  178.     usage();
  179.     return EXIT_FAILURE;
  180.   }
  181.   /* Complain if too many arguments. */
  182.   if((next_arg + 1) < argc) {
  183.     fprintf(stderr, "%s: Too many arguments\n\n", tool);
  184.     usage();
  185.     return EXIT_FAILURE;
  186.   }
  187.   /* Ensure only one set of driver options is used. */
  188.   if(((driver_name || buffer_size) && (block_time || stype)) ||
  189.     (!driver_name && block_size && block_time)) {
  190.     fprintf(stderr, "%s: Conflicting options\n", tool);
  191.     usage();
  192.     return EXIT_FAILURE;
  193.   }
  194.   /* Driver name must come with buffer and block sizes. */
  195.   if(driver_name && (!block_size || !buffer_size)) {
  196.     fprintf(stderr, "%s: Missing driver options\n", tool);
  197.     usage();
  198.     return EXIT_FAILURE;
  199.   }
  200.  
  201.   /* Work out driver details if sound type was given. */
  202.   if(!driver_name) {
  203.     /* Find the driver name. */
  204.     if(stype->precision == 8) {
  205.       if(stype->linear_sound) {
  206.         if(stype->unsigned_sound)
  207.           driver_name = "SoundU8";
  208.           else driver_name = "SoundS8";
  209.       }
  210.       else driver_name = "SoundE8";
  211.     }
  212.     else if(stype->precision == 16 && stype->linear_sound &&
  213.         !stype->unsigned_sound) driver_name = "SoundS16";
  214.     /* Complain if there isn't a driver for this type. */
  215.     if(!driver_name) {
  216.       fprintf(stderr, "%s: No suitable driver\n", tool);
  217.       return EXIT_FAILURE;
  218.     }
  219.     /* Add number of channels to driver. */
  220.     {
  221.       int len = strlen(driver_name);
  222.       char *copy = malloc(len + 3 + 1);
  223.       if(!copy) abort();
  224.       memcpy(copy, driver_name, len + 1);
  225.       if(stype->channels > 1)
  226.         sprintf(copy + len, "x%i", stype->channels);
  227.       driver_name = copy;
  228.     }
  229.  
  230.     /* Work out block size or time given other variable. */
  231.     if(block_size) {
  232.       block_time = block_size * 8 * 100 / (int) frequency /
  233.         stype->channels / stype->precision;
  234.     }
  235.     else {
  236.       if(!block_time) block_time = DEFAULT_BLOCK_TIME;
  237.       block_size = (size_t)
  238.     ((double) block_time / 100.0
  239.     * frequency
  240.     * stype->channels
  241.     * stype->precision
  242.     + 7) / 8;
  243.     }
  244.     /* Work out buffer size. */
  245.     buffer_size = ((size_t)
  246.     ((double) block_time / 100.0    /* Seconds per block. */
  247.     * frequency            /* Account for sample rate. */
  248.     * stype->channels        /* Account for number of channels. */
  249.     * 1.01                /* Add 1% extra space. */
  250.     * stype->precision        /* Number of bits per sample. */
  251.     + 7) / 8            /* Round up, and bits -> bytes. */
  252.     + 12 + 4            /* Add some more. */
  253.     + 3) & ~3;            /* Word align size. */
  254.   }
  255.  
  256.   /* Print driver info, and block/buffer sizes. */
  257.   if(verbose) {
  258.     printf("%s: Replay frequency %i Hz\n", tool, (int) frequency);
  259.     printf("%s: Using driver `%s', block size %i bytes (time %i cs), "
  260.     "buffer size %i bytes\n", tool, driver_name, block_size, block_time,
  261.     buffer_size);
  262.   }
  263.  
  264.   /* Initialise driver and control block. */
  265.   atexit(finalise);
  266.   driver = replaydriver_load(driver_name);
  267.   if(!driver) {
  268.     fprintf(stderr, "%s: Couldn't load driver `%s'\n", tool, driver_name);
  269.     code = EXIT_FAILURE;
  270.     goto finish;
  271.   }
  272.   control = replaydriver_control_create();
  273.   if(!control) {
  274.     fprintf(stderr, "%s: Couldn't create control block\n", tool);
  275.     code = EXIT_FAILURE;
  276.     goto finish;
  277.   }
  278.   /* Fill in information. */
  279.   replaydriver_set_rate(control, frequency);
  280.  
  281.   /* Do timing check while creating buffer and opening file. */
  282.   replaydriver_timecheck_start(driver, control, &time_check);
  283.   buffer = replaydriver_buffer_create(buffer_size, driver);
  284.   if(!buffer) {
  285.     fprintf(stderr, "%s: Couldn't create sound buffer\n", tool);
  286.     code = EXIT_FAILURE;
  287.     goto finish;
  288.   }
  289.   {
  290.     const char *filename = argv[next_arg];
  291.     if(filename[0] == '-' && !filename[1]) filename = 0;
  292.     if(filename) file = fopen(filename, "rb");
  293.       else file = stdin;
  294.     if(!file && filename) {
  295.       fprintf(stderr, "%s: Couldn't open file `%s' for input\n",
  296.         tool, filename);
  297.       code = EXIT_FAILURE;
  298.       goto finish;
  299.     }
  300.   }
  301.   replaydriver_timecheck_finish(driver, control, &time_check);
  302.  
  303.   /* Start the playback. */
  304.   replaydriver_play(driver, control);
  305.   is_playing = 1;
  306.   while(1) {
  307. #ifdef RISCOS
  308.     /* Are we running in a TaskWindow? */
  309.     bool tw;
  310.     if(!xtaskwindowtaskinfo_window_task(&tw) && tw) {
  311.       /* Wait for buffer to become empty. */
  312.       const int *poll_word = replaydriver_get_poll_word(driver, control);
  313.       if(poll_word) xupcall_sleep((int *) poll_word, 0);
  314.     }
  315. #endif
  316.     /* Has a buffer become empty? */
  317.     if(replaydriver_is_hungry(driver)) {
  318.       size_t size;
  319.       void *data;
  320.  
  321.       /* Has playback finished entirely? */
  322.       if(feof(file) && replaydriver_buffers_empty(driver)) break;
  323.  
  324.       /* Allocate temp buffer. */
  325.       assert(block_size > 0);
  326.       size = block_size;
  327.       data = replaydriver_temp_alloc(buffer, size);
  328.       if(!data) {
  329.         if(!feed_error) fprintf(stderr, "%s: Can't allocate feeding "
  330.         "buffer (free some memory, and I'll continue)\n", tool);
  331.         feed_error = 1;
  332.         continue;
  333.       }
  334.  
  335.       /* Read data from file. */
  336.       size = fread(data, 1, size, file);
  337.  
  338.       /* Feed driver with data. */
  339.       replaydriver_feed(driver, control, data, size);
  340.       if(replaydriver_buffer_check(buffer))
  341.         fprintf(stderr, "%s: Buffer overrun (check word currupted) -- "
  342.         "if the module area was corrupted "
  343.         "you might want to worry\n", tool);
  344.       replaydriver_temp_free(buffer, data);
  345.       feed_error = 0;
  346.     }
  347.   }
  348.   replaydriver_stop(driver, control);
  349.  
  350.   /* Finalise. */
  351. finish:
  352.   finalise();
  353.  
  354. #ifdef MemCheck_MEMCHECK
  355.   MemCheck_OutputBlocksInfo();
  356. #endif
  357.  
  358.   return code;
  359. }
  360.  
  361. /* Free up everything.  Called by main(), and also used as an atexit
  362.    handler. */
  363. static void finalise(void)
  364. {
  365.   if(is_playing) { replaydriver_stop(driver, control); is_playing = 0; }
  366.     else replaydriver_timecheck_abort(driver, control, &time_check);
  367.  
  368.   replaydriver_destroy(driver);            driver = 0;
  369.   replaydriver_control_destroy(control);    control = 0;
  370.   replaydriver_buffer_destroy(buffer);        buffer = 0;
  371.   if(file && file != stdin) fclose(file);    file = 0;
  372. }
  373.  
  374.  
  375. /* Create default sound_type structure.  Returns 0 if malloc failed. */
  376. sound_type *soundtype_create(void)
  377. {
  378.   sound_type *obj = malloc(sizeof(sound_type));
  379.   if(!obj) return 0;
  380.  
  381.   /* Set default sound type (-O8lsM). */
  382.   obj->precision = 8;
  383.   obj->linear_sound = 1;
  384.   obj->unsigned_sound = 0;
  385.   obj->channels = 1;
  386.   return obj;
  387. }
  388.  
  389. /* Read sound type command line options. */
  390. void soundtype_getopt(sound_type *obj, const char *opt)
  391. {
  392.   const char *c;
  393.   if(!obj) return;
  394.  
  395.   for(c = opt; *c; ++c) switch(*c) {
  396.     case '8':  obj->precision = 8; continue;
  397.     case '1':  obj->precision = 16; continue;
  398.     case 'l':  obj->linear_sound = 1; continue;
  399.     case 'U':  obj->linear_sound = 0; continue;
  400.     case 's':  obj->unsigned_sound = 0; continue;
  401.     case 'u':  obj->unsigned_sound = 1; continue;
  402.     case 'M':  obj->channels = 1; continue;
  403.     case 'S':  obj->channels = 2; continue;
  404.     default:
  405.       fprintf(stderr, "%s: Unknown argument, `-O%c'\n", tool, *c);
  406.       exit(EXIT_FAILURE);
  407.   }
  408. }
  409.